<?php

namespace App\Models\OpenShop\InvestmentPromotion;

use App\Enums\BaseErrorCode;
use App\Enums\OpenShop\InvestmentPromotion\ContractStatusEnum;
use App\Enums\OpenShop\InvestmentPromotion\ErrCode;
use App\Enums\OpenShop\InvestmentPromotion\OrganizationManageEnum;
use App\Enums\OpenShop\InvestmentPromotion\SignOrderEnum;
use App\Enums\OpenShop\InvestmentPromotion\UserContractEnum;
use App\Enums\OpenShop\MissionTemplateEnum;
use App\Exceptions\Custom\CustomHttpResponseException;
use App\Exceptions\Custom\ResponseHttpException;
use App\Library\FeiShu\SendFs;
use App\Models\BaseModel;
use App\Models\Clickhouse\Guanyuan\GyCloseShopCHModel;
use App\Models\Guanyuan\GuanyuanSyncShopBusinessModel;
use App\Models\OpenShop\Common\OpenShopOrderManagerModel;
use App\Models\OpenShop\ShopCheckAccept\OpenShopCheckAcceptModel;
use App\Models\OpenShop\SiteLocation\OpenShopSiteLocationModel;
use App\Models\OpenShop\StartBusiness\OpenShopStartBusinessModel;
use App\Models\Shop\ShopModel;
use App\Models\Traits\KfSoftDeletes;
use App\Models\Traits\KfUid;
use App\Service\OpenShop\Feishu\MissionTemplateService;
use App\Service\OpenShop\Feishu\OrganizationManageService;
use App\Service\OpenShop\Feishu\PermissionService;
use App\Service\OpenShop\UserContractService;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
use Illuminate\Testing\Fluent\Concerns\Has;
use Kuafu\GeneralFs\BuildFs;
use Tinderbox\Clickhouse\Exceptions\ClientException;
use function Symfony\Component\String\s;

class OpenShopSignOrderModel extends BaseModel
{
    use KfUid;

    const DISCLOSURE_STATUS_ON  = 1;  //披露单已上传
    const DISCLOSURE_STATUS_OFF = 0;  //披露单未上传

    protected $table = 'open_shop_sign_order';

    protected $fillable = [
        'mainProcessId',
        'orderNo',
        'shopCode',
        'step',
        'shouldPayPrice',
        'payPrice',
        'payStatus', // 已支付定金的单子payStatus=2，部分支付 SignOrderEnum::PAY_STATUS_PART；补完尾款后payStatus=3
        'signTime',
        'investmentManagerName',
        'investmentManagerId',
        'status',
        'merchantUserId',
        'provideShopCode',
        'orderType',
        'shopTag',
        'contractStatus',
        'dissolveStatus',
        'isPlaceholder',
        'invalidateType',
        'invalidedReason'
    ];

    const CONTRACT_STATUS_CHANGE = 0;
    const CONTRACT_STATUS_NORMAL = 1;


    const DISSOLVE_STATUS_NORMAL = 0;
    const DISSOLVE_STATUS_CANCEL = 1;
    const DISSOLVE_STATUS_CLOSE  = 2;

    const ORDER_SHOULD_PRICE_FROM = [
        "initialFee",
        "technologyTransferFee",
        "deposit",
        "managementCost",
        "decorationDeposit",
        "guidanceFee",
    ];

    const INVALID_TYPE_1 = 1;
    const INVALID_TYPE_2 = 2;
    const INVALID_TYPE_3 = 3;
    const INVALID_TYPE_4 = 4;

    const INVALID_TYPE_MAP = [
        self::INVALID_TYPE_1 => "放弃支付",
        self::INVALID_TYPE_2 => "放弃签署",
        self::INVALID_TYPE_3 => "门店解约",
        self::INVALID_TYPE_4 => "门店闭店",
    ];

    /**
     * 是否预占点位
     */
    const IS_PLACEHOLDER_YES = 1; // 是
    const IS_PLACEHOLDER_NO  = 0; // 否

    // 测试店铺号start
    const SHOP_CODE_TEST = 9000;

    const INVALIDATE_TYPE_1    = 1;
    const INVALIDATE_TYPE_2    = 2;
    const INVALIDATE_TYPE_3    = 3;
    const INVALIDATE_TYPE_4    = 4;
    const INVALIDATE_TYPE_5    = 5;
    const INVALIDATE_TYPE_TEXT = [
        self::INVALIDATE_TYPE_1 => "产研测试",
        self::INVALIDATE_TYPE_2 => "合同内容有误",
        self::INVALIDATE_TYPE_3 => "开业前解约退款",
        self::INVALIDATE_TYPE_4 => "闭店解约",
        self::INVALIDATE_TYPE_5 => "其他",
    ];

    /**
     * @return HasOne
     */
    public function baseInfoRel(): HasOne
    {
        return $this->hasOne(OpenShopUserBaseInfoModel::class, "orderNo", "orderNo");
    }

    /**
     * @return HasOne
     */
    public function contractRel(): HasOne
    {
        return $this->hasOne(OpenShopUserContractModel::class, "orderNo", "orderNo")
                    ->whereIn("contractType", OpenShopUserContractModel::OPEN_SHOP_CONTRACT_GROUP)
                    ->orderBy("id", "desc");
    }

    /**
     * @return HasMany
     */
    public function allContractRel(): HasMany
    {
        return $this->hasMany(OpenShopUserContractModel::class, "orderNo", "orderNo")
                    ->where("detailStatus", ContractStatusEnum::COMPLETE)
                    ->orderBy("id", "desc");
    }

    /**
     * @return HasMany
     */
    public function allOrderDetailContractRel(): HasMany
    {
        return $this->hasMany(OpenShopUserContractModel::class, "orderNo", "orderNo")
                    ->whereIn("detailStatus", [ContractStatusEnum::COMPLETE, ContractStatusEnum::SIGNING])
                    ->orderBy("id", "desc");
    }

    /**
     * @return HasOne
     */
    public function lastFinishContractRel(): HasOne
    {
        return $this->hasOne(OpenShopUserContractModel::class, "orderNo", "orderNo")
                    ->whereIn("contractType", [
                        UserContractEnum::CONTRACT_TYPE_1,
                        UserContractEnum::CONTRACT_TYPE_2,
                        UserContractEnum::CONTRACT_TYPE_8
                    ])
                    ->where("detailStatus", ContractStatusEnum::COMPLETE)
                    ->orderBy("id", "desc");
    }

    /**
     * @return HasOne
     */
    public function normalContractRel(): HasOne
    {
        return $this->hasOne(OpenShopUserContractModel::class, "orderNo", "orderNo")
                    ->whereIn("contractType", OpenShopUserContractModel::OPEN_SHOP_CONTRACT_GROUP)
                    ->where("contractExtNo", "<>", "")
                    ->orderBy("id", "desc");
    }

    /**
     * @return HasOne
     */
    public function invalidContractRel(): HasOne
    {
        return $this->hasOne(OpenShopUserContractModel::class, "orderNo", "orderNo")
                    ->whereIn("contractType", [
                        UserContractEnum::CONTRACT_TYPE_3,
                        UserContractEnum::CONTRACT_TYPE_4,
                        UserContractEnum::CONTRACT_TYPE_5,
                        UserContractEnum::CONTRACT_TYPE_6,
                        UserContractEnum::CONTRACT_TYPE_7,
                    ])
                    ->orderBy("id", "desc");
    }

    /**
     * @return HasOne
     */
    public function identityCardRel(): HasOne
    {
        return $this->hasOne(
            OpenShopUserIdentityCardModel::class,
            'orderNo',
            'orderNo',
        );
    }

    /**
     * @return HasOne
     */
    public function userExperienceRel(): HasOne
    {
        return $this->hasOne(
            OpenShopUserExperienceModel::class,
            'orderNo',
            'orderNo',
        );
    }

    /**
     * @return HasOne
     */
    public function siteLocationRel(): HasOne
    {
        return $this->hasOne(OpenShopSiteLocationModel::class, "orderNo", "orderNo")->whereIn('source',
            [OpenShopSiteLocationModel::SOURCE_OPEN_SHOP, OpenShopSiteLocationModel::SOURCE_HOUSING]);
    }

    /**
     * @return HasOne
     */
    public function progressRel(): HasOne
    {
        return $this->hasOne(OpenShopMainProcessModel::class, "id", "mainProcessId");
    }

    /**
     * @return HasMany
     */
    public function payHistoryRel(): HasMany
    {
        return $this->hasMany(OpenShopSignOrderPayHistoryModel::class, "orderNo", "orderNo");
    }

    /**
     * @return HasOne
     */
    public function shopLocationRel(): HasOne
    {
        return $this->hasOne(OpenShopLocationModel::class, "orderNo", "orderNo");
    }

    /**
     * @return HasOne
     */
    public function checkAcceptRel(): HasOne
    {
        return $this->hasOne(OpenShopCheckAcceptModel::class, "orderNo", "orderNo");
    }

    /**
     * @return HasOne
     */
    public function spaceDesignRel(): HasOne
    {
        return $this->hasOne(OpenShopSpaceDesignModel::class, "orderNo", "orderNo");
    }

    /**
     * @return HasMany
     */
    public function processDetailRel(): HasMany
    {
        return $this->hasMany(OpenShopProcessDetailModel::class, 'mainProcessId', 'mainProcessId');
    }

    public function startBusinessRel(): HasOne
    {
        return $this->hasOne(OpenShopStartBusinessModel::class, "orderNo", "orderNo");

    }

    /**
     * @return HasMany
     */
    public function siteMissionsRel(): HasMany
    {
        return $this->hasMany(OpenShopSiteMissionModel::class, 'orderNo', 'orderNo');
    }

    public function signOrderLogRel(): HasMany
    {
        return $this->hasMany(OpenShopSignOrderLogModel::class, 'orderId', 'id');
    }

    public function softLogisticsModelRel(): HasMany
    {
        return $this->hasMany(OpenShopSpaceDesignSoftLogisticsModel::class, 'orderNo', 'orderNo');
    }

    public function addressRel(): HasOne
    {
        return $this->hasOne(OpenShopAddressModel::class, 'orderNo', 'orderNo');
    }

    /**
     * @param string $orderNo
     * @param bool $withBaseInfo
     * @param bool $withContract
     * @param bool $withIdentityCard
     * @param bool $withWorkExperience
     * @param bool $withPayHistory
     * @param bool $withProgress
     * @param bool $withExtContract
     * @param bool $withContractTemplate
     * @param int $dataPermission
     * @param bool $withShopLocation
     * @param bool $siteLocationRel
     * @param bool $tagsMappingRel
     * @param bool $startBusinessRel
     * @param bool $siteMissionsRel
     * @return array|Builder[]|Collection
     */
    public static function orderDetail(
        string $orderNo,
        bool $withBaseInfo = false,
        bool $withContract = false,
        bool $withIdentityCard = false,
        bool $withWorkExperience = false,
        bool $withPayHistory = false,
        bool $withProgress = false,
        bool $withExtContract = false,
        bool $withContractTemplate = false,
        int $dataPermission = 0,
        bool $withShopLocation = false,
        bool $siteLocationRel = false,
        bool $tagsMappingRel = false,
        bool $startBusinessRel = false,
        bool $siteMissionsRel = false
    ) {
        $thisTable = (new OpenShopSignOrderModel())->getTable();
        return self::query()
                   ->when(is_array($orderNo), function ($query) use ($orderNo, $thisTable) {
                       $query->whereIn($thisTable . '.orderNo', $orderNo);
                   }, function ($query) use ($orderNo, $thisTable) {
                       $query->where($thisTable . '.orderNo', $orderNo);
                   })
                   ->when($withBaseInfo, function ($query) {
                       $query->with('baseInfoRel');
                   })
                   ->when($withContract, function ($query) use ($withExtContract, $withContractTemplate) {
                       $query->with([
                           'contractRel' => function ($query) use ($withExtContract, $withContractTemplate) {
                               $query->when($withExtContract, function ($query) {
                                   $query->with('userExtContractRel');
                               });
                               $query->when($withContractTemplate, function ($query) {
                                   $query->with('templateRel');
                               });
                           }
                       ]);
                   })
                   ->when($withIdentityCard, function ($query) {
                       $query->with('identityCardRel');
                   })
                   ->when($withWorkExperience, function ($query) {
                       $query->with('userExperienceRel');
                   })
                   ->when($withPayHistory, function ($query) {
                       $query->with('payHistoryRel');
                   })
                   ->when($withProgress, function ($query) {
                       $query->with('progressRel');
                   })
                   ->when($withShopLocation, function ($query) {
                       $query->with('shopLocationRel');
                   })
                   ->when(
                       $siteLocationRel, function ($query) {
                       $query->with('siteLocationRel');
                   }
                   )
                   ->when(
                       $tagsMappingRel, function ($query) {
                       $query->with('tagsMappingRel');
                   }
                   )
                   ->when(
                       $startBusinessRel, function ($query) {
                       $query->with('startBusinessRel');
                   }
                   )
                   ->when(
                       $siteMissionsRel, function ($query) {
                       $query->with('siteMissionsRel');
                   }
                   )
                   ->get();
    }

    /**
     * 通过shopId获取signOrder对象
     * @param int $shopId
     * @return Builder|Model|object|null
     */
    public static function getModelByShopId(int $shopId)
    {
        $mainProcessId = OpenShopMainProcessModel::getIdByShopId($shopId);

        return self::query()
                   ->where('mainProcessId', $mainProcessId)
                   ->first();
    }

    /**
     * 单笔支付成功
     * @param int $payPrice
     * @return $this
     */
    public function onePaySuccess(int $payPrice): self
    {
        // 已支付金额累加
        $this->payPrice += $payPrice;

        if ($this->payPrice == $this->shouldPayPrice) {

            // 整单支付完成
            $this->payStatus = SignOrderEnum::PAY_STATUS_OK;

            if ($this->step < SignOrderEnum::STEP_4) {
                $this->step = SignOrderEnum::STEP_4;
            }

        } elseif ($this->payPrice < $this->shouldPayPrice) {

            // 部分支付完成
            $this->payStatus = SignOrderEnum::PAY_STATUS_PART;
            if ($this->step < SignOrderEnum::STEP_3) {
                $this->step = SignOrderEnum::STEP_3;
            }

            // 定金模式下，付完定金了
            list($isFinalPay, $lastPrice, $depositAmount) = UserContractService::getLastPrice($this);
            if (!$isFinalPay && $lastPrice == 0 && $this->step == SignOrderEnum::STEP_3) {
                $this->step = 4;
            }

        } else {

            // 支付金额超过应付
            throw new CustomHttpResponseException(
                ErrCode::errCodeTrans(ErrCode::CODE_PAY_PRICE_OVERFLOW)
            );
        }

        $this->save();

        return $this;
    }

    /**
     * 更新shopCode
     * @param int $shopCode
     * @return $this
     */
    public function saveShopCode(int $shopCode): self
    {
        $this->shopCode = $shopCode;
        $this->save();

        return $this;
    }

    public static function updateStepByOrderNo($orderNo, $step)
    {
        self::query()
            ->where('orderNo', $orderNo)
            ->update(['step' => $step, 'signTime' => time()]);
    }

    public static function saveMerchantUserId(string $orderNo, int $userId): int
    {
        return self::query()->where('orderNo', $orderNo)->update(['merchantUserId' => $userId]);
    }

    /**
     * @param string $orderNo
     * @return \Illuminate\Database\Eloquent\HigherOrderBuilderProxy|mixed
     */
    public static function getOrderIdByOrderNo(string $orderNo)
    {
        try {
            return self::query()->where("orderNo", $orderNo)->first()->id;
        } catch (\Exception $exception) {
            handleException($exception);
        }
    }

    /**
     * @return HasMany
     */
    public function orderManagerRel(): HasMany
    {
        return $this->hasMany(OpenShopOrderManagerModel::class, "mainProcessId", "mainProcessId");
    }

    /**
     * 开发顾问
     *
     * @return HasOne
     */
    public function developmentConsultantRel(): HasOne
    {
        return $this->hasOne(OpenShopOrderManagerModel::class, "mainProcessId", "mainProcessId")
                    ->where("type", OpenShopOrderManagerModel::MANAGER_TYPE_2);
    }

    /**
     * @return HasMany
     */
    public function spaceDesignSoftOutfitRel(): HasMany
    {
        return $this->hasMany(OpenShopSpaceDesignSoftOutfitMappingModel::class, "orderNo", "orderNo");
    }

    /**
     * @return HasMany
     */
    public function spaceDesignCostRel(): HasMany
    {
        return $this->hasMany(OpenShopSpaceDesignCostMappingModel::class, "orderNo", "orderNo");
    }

    public function getKfShopCodeAttribute()
    {
        return str_pad($this->shopCode, 4, "0", STR_PAD_LEFT);
    }

    public static function getOrderType(string $orderNo): int
    {
        return (int)self::query()->where('orderNo', $orderNo)->value('orderType');
    }

    public static function saveShopTag(string $orderNo, array $shopTag): int
    {
        return self::query()->where('orderNo', $orderNo)->update([
            'shopTag' => json_encode($shopTag, JSON_UNESCAPED_UNICODE)
        ]);
    }

    /**
     * 获取作废的订单数量
     * @param int $stepStatus
     * @return int
     */
    public static function countDel(array $orderPermission): int
    {
        return self::query()
                   ->when(!$orderPermission['isAll'], function ($query) use ($orderPermission) {
                       $query->whereRaw($orderPermission['rawSql']);
                   })
                   ->where('status', SignOrderEnum::ORDER_STATUS_NO)
                   ->count();
    }

    /**
     * 获取解约订单数量
     * @param array $orderPermission
     * @return int
     */
    public static function countCancelOrder(array $orderPermission): int
    {
        return self::query()
                   ->when(!$orderPermission['isAll'], function ($query) use ($orderPermission) {
                       $query->whereRaw($orderPermission['rawSql']);
                   })
                   ->where('status', '=', SignOrderEnum::ORDER_STATUS_NO)
                   ->where('dissolveStatus', OpenShopSignOrderModel::DISSOLVE_STATUS_CANCEL)
                   ->count();
    }

    /**
     * 获取闭店订单数量
     * @param array $orderPermission
     * @return int
     */
    public static function countCloseOrder(array $orderPermission): int
    {
        return self::query()
                   ->when(!$orderPermission['isAll'], function ($query) use ($orderPermission) {
                       $query->whereRaw($orderPermission['rawSql']);
                   })
                   ->where('status', '=', SignOrderEnum::ORDER_STATUS_NO)
                   ->where('dissolveStatus', OpenShopSignOrderModel::DISSOLVE_STATUS_CLOSE)
                   ->count();
    }

    /**
     * 获取披露单数量
     * @param array $orderPermission
     * @return int
     */
    public static function countDisclosure(array $orderPermission): int
    {
        return self::query()
                   ->when(!$orderPermission['isAll'], function ($query) use ($orderPermission) {
                       $query->whereRaw($orderPermission['rawSql']);
                   })
                   ->where('status', '!=', SignOrderEnum::ORDER_STATUS_NO)
                   ->where('disclosureStatus', OpenShopSignOrderModel::DISCLOSURE_STATUS_OFF)
                   ->count();
    }

    /**
     * @return HasOne
     */
    public function mainProcessRel(): HasOne
    {
        return $this->hasOne(OpenShopMainProcessModel::class, 'id', 'mainProcessId');
    }

    /**
     * @param string $shopCode
     * @return \Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection
     */
    public static function getDataByShopCode(string $shopCode)
    {
        return self::query()->where('shopCode', $shopCode)->get();
    }

    /**
     * 根据订单ID 获取订单详情
     *
     * @param string $orderNo
     * @return Builder|Model|object|null
     */
    public static function getOrderDetailByOrderNo(string $orderNo)
    {
        return self::query()->where("orderNo", $orderNo)->first();
    }

    /**
     * @return HasMany
     */
    public function tagsMappingRel(): HasMany
    {
        return $this->hasMany(OpenShopOrderTagsMappingModel::class, "orderNo", "orderNo");
    }

    /**
     * @return HasOne
     */
    public function lastSiteMissionsRel(): HasOne
    {
        return $this->hasOne(OpenShopSiteMissionModel::class, 'orderNo', 'orderNo')->orderByDesc("id");
    }

    /** 超时未支付的订单
     * @return HasOne
     */
    public function contractTimeOutNoPayRel(): HasOne
    {
        return $this->hasOne(OpenShopUserContractModel::class, "orderNo", "orderNo")
                    ->where('createdAt', '<', (time() - SignOrderEnum::SIGN_DEADLINE_SECONDS))
                    ->where('createdAt', '>', (time() - SignOrderEnum::SIGN_DEADLINE_SECONDS) - 7200);
    }

    /**
     * 关联门店详情
     * @return HasOne
     */
    public function shopRel(): HasOne
    {
        return $this->hasOne(ShopModel::class, 'shopCode', 'shopCode');
    }

    /**
     * 获取已开业门店
     *
     * @param int $regionSelectionType
     * @param string $region
     * @return array
     * @throws ClientException
     */
    public static function getNormalShop(int $regionSelectionType, string $region): array
    {
        $gyCloseShopCHModelBaseQuery = GyCloseShopCHModel::buildHomeSearchQuery(
            $regionSelectionType, $region
        );
        $gyNormalShopCode            = (clone $gyCloseShopCHModelBaseQuery)
            ->where("status", "正常营业")
            ->get()
            ->pluck("berthNumber")
            ->map(function ($v) {
                return (int)$v;
            })
            ->toArray();

        $gySuspendBusinessShopCode = (clone $gyCloseShopCHModelBaseQuery)
            ->where("status", "暂停营业")
            ->get()
            ->pluck("berthNumber")
            ->map(function ($v) {
                return (int)$v;
            })
            ->toArray();

        $shopBusinessBaseQuery = GuanyuanSyncShopBusinessModel::buildHomeSearchQuery(
            $regionSelectionType, $region
        );

        $yesterday         = date('Y-m-d', strtotime('-1 day'));
        $businessShopCodes = $shopBusinessBaseQuery->where('date', $yesterday)
                                                   ->pluck('code')
                                                   ->toArray();// 昨天有数据的门店

        $signOrderTable   = (new OpenShopSignOrderModel())->getTable();
        $mainProcessTable = (new OpenShopMainProcessModel())->getTable();


        return array_unique(array_merge(
            OpenShopSignOrderModel::query()->leftJoin(
                $mainProcessTable,
                "$mainProcessTable.id",
                "$signOrderTable.mainProcessId"
            )->whereIn(
                "$signOrderTable.status", [
                    SignOrderEnum::ORDER_STATUS_OK,
                    SignOrderEnum::ORDER_STATUS_COMPLETE
                ]
            )->whereIn(
                "$signOrderTable.ShopCode",
                array_unique(array_merge($gyNormalShopCode, $businessShopCodes))
            )->whereIn(
                "$mainProcessTable.nowStepStatus", [
                    MissionTemplateEnum::STEP_STATUS_18,
                    MissionTemplateEnum::STEP_STATUS_999
                ]
            )->groupBy(
                "$signOrderTable.shopCode"
            )->select(["$signOrderTable.shopCode"])->pluck("shopCode")->toArray(),
            $gySuspendBusinessShopCode
        ));
    }
}
